﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mimoza.Transport;

namespace Mimoza.DBLayer.Transport
{
    public interface ICollection 
    {
        Guid CollectionID { get; }

        TransferObject Current { get; }
        TransferObject MoveNext();
        TransferObject Reset();
        TransferObject Dispose();
    }

    public class CollectionTransfer
    {
        public enum Operation
        {
            None,
            Reset,
            MoveNext,
            Current,
            Dispose
        };

        public CollectionTransfer()
        {
        }

        public CollectionTransfer(Guid id)
        {
            ID = id;
        }

        public Guid ID
        {
            get { return m_collectionId; }
            set { m_collectionId = value; }
        }

        public Operation OperationType
        {
            get { return m_operation; }
            set { m_operation = value; }
        }

        Guid m_collectionId;
        Operation m_operation = Operation.None;
    }
    
    public class CollectionRequest : TransferObjectGenericAnyReply<CollectionRequest>
    {
        public CollectionRequest()
        {
        }

        public CollectionRequest(Guid id)
        {
            CollectionTransfer.ID = id;
        }

        public CollectionTransfer CollectionTransfer = new CollectionTransfer();
    }

    public class CollectionReply<TRecord> :
        TransferObjectGenericReply<CollectionReply<TRecord>>
        where TRecord : class, new()
    {
        public enum OperationResult
        {
            None,
            Error,
            Ok,
            True,
            False
        };

        public OperationResult Result
        {
            get { return m_operation; }
            set { m_operation = value; }
        }

        public TRecord Object
        {
            get
            {
                return m_object;
            }
            set
            {
                m_object = value;
            }
        }


        OperationResult m_operation = OperationResult.None;
        TRecord m_object = null;
    }

    public interface ICollectionRequestProcessor
    {
        TransferObject ProcessRequest(CollectionRequest request);
    }

    public class CollectionTranferRequest<TRecord> :
        TransferObjectGeneric<CollectionTranferRequest<TRecord>, CollectionReply<TRecord>>
        where TRecord : class, new()
    {
        public static void SetProcessor(ICollectionRequestProcessor processor)
        {
            m_processor = processor;
        }

        override public CollectionReply<TRecord> ProcessRequest()
        {
            return m_processor.ProcessRequest(Request) as CollectionReply<TRecord>;
        }

        public CollectionRequest Request = new CollectionRequest();

        static ICollectionRequestProcessor m_processor = null;
    }

    public class Collection<TRecord> :
        ICollection
        where TRecord : class, new()
    {
        public Collection(IEnumerable<TRecord> enumerator)
        {
            Init(Guid.NewGuid(),enumerator);
        }

        public Collection(Guid id, IEnumerable<TRecord> enumerator)
        {
            Init(id, enumerator);
        }

        static Collection()
        {
            RegisterInTransport();
        }

        public void Init(Guid id, IEnumerable<TRecord> enumerator)
        {
            m_ID = id;
            m_enumerableObject = enumerator;
            m_enumerator = m_enumerableObject != null ? m_enumerableObject.GetEnumerator() :
                null;
        }

        static public void RegisterInTransport()
        {
            CollectionReply<TRecord>.RegisterInTransport();
        }

        public Guid CollectionID
        {
            get { return m_ID; }
        }

        public TransferObject Current
        {
            get
            {
                if (m_enumerator == null)
                {
                    return CreateErrorReply();
                }

                TRecord currentObject = m_enumerator.Current;

                CollectionReply<TRecord> reply = new CollectionReply<TRecord>();
                reply.Result = CollectionReply<TRecord>.OperationResult.Ok;
                reply.Object = currentObject;

                return reply;
            }
        }

        public TransferObject MoveNext()
        {
            if (m_enumerator == null)
            {
                return CreateErrorReply();
            }

            bool bMoveResult = m_enumerator.MoveNext();

            CollectionReply<TRecord> reply = new CollectionReply<TRecord>();
            reply.Result = bMoveResult ? CollectionReply<TRecord>.OperationResult.True :
                CollectionReply<TRecord>.OperationResult.False;

            return reply;
        }

        public TransferObject Reset()
        {
            if (m_enumerator == null)
            {
                return CreateErrorReply();
            }

            m_enumerator.Reset();

            return CreateOkReply();
        }

        public TransferObject Dispose()
        {
            m_enumerator = null;
            m_enumerableObject = null;

            return CreateOkReply();
        }

        TransferObject CreateOkReply()
        {
            CollectionReply<TRecord> reply = new CollectionReply<TRecord>();
            reply.Result = CollectionReply<TRecord>.OperationResult.Ok;

            return reply;
        }

        TransferObject CreateErrorReply()
        {
            CollectionReply<TRecord> reply = new CollectionReply<TRecord>();
            reply.Result = CollectionReply<TRecord>.OperationResult.Error;

            return reply;
        }

        Guid m_ID;
        IEnumerable<TRecord> m_enumerableObject;
        IEnumerator<TRecord> m_enumerator;
    }

    public class ClientCollectionEnumerable<TRecord> :
        IEnumerable<TRecord>,
        IEnumerator<TRecord>
        where TRecord : class, new()
    {
        public ClientCollectionEnumerable(Connection conn, Guid collectionId)
        {
            m_ID = collectionId;
            m_connection = conn;
        }
        
        public TRecord Current 
        { 
            get
            {
                CollectionReply<TRecord> reply = MakeRequest(CollectionTransfer.Operation.Current);
                return reply.Object;
            }
        }
        
        object System.Collections.IEnumerator.Current
        {
            get
            {
                return Current as object;
            }
        }

        public bool MoveNext()
        {
            CollectionReply<TRecord> reply = MakeRequest(CollectionTransfer.Operation.MoveNext);
            bool bRes = (reply.Result == CollectionReply<TRecord>.OperationResult.True)?
                true:false;

            return bRes;
        }

        public void Reset()
        {
            MakeRequest(CollectionTransfer.Operation.Reset);
        }

        public IEnumerator<TRecord> GetEnumerator()
        {
            return this;
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this;
        }

        public void Dispose()
        {
            MakeRequest(CollectionTransfer.Operation.Dispose);
        }

        CollectionReply<TRecord> MakeRequest(CollectionTransfer.Operation operation)
        {
            CollectionRequest request = new CollectionRequest(m_ID);
            request.CollectionTransfer.OperationType = operation;

            TransferObject reply;
            m_connection.MakeRequest(request, out reply);

            CollectionReply<TRecord> collectionReply = reply as CollectionReply<TRecord>;

            if (collectionReply == null)
            {
                throw new ArgumentException();
            }
            if (collectionReply.Result == CollectionReply<TRecord>.OperationResult.Error)
            {
                throw new ArgumentException();
            }

            return collectionReply;
        }

        Guid m_ID;
        Connection m_connection;
    }   
}
